package com.anlogic.sdk.linker;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LScript {
	private String stacksize;

	private String heapsize;

	private List<Memory> memories = new ArrayList<Memory>();

	private List<MemRangeSection> memRangeSections = new ArrayList<MemRangeSection>();

	private static final Pattern memoryRegionAreaPattern = Pattern.compile("MEMORY\\s*\\{([\\s\\S]*)\\}");
	private static final Pattern memoryRegionPattern = Pattern.compile("(\\w+)(?:.*):\\s*ORIGIN\\s*=\\s*(\\w+)\\s*,\\s*LENGTH\\s*=\\s*(\\w+)");
	private static final Pattern memoryRegionAttrPattern = Pattern.compile("\\w+\\s*\\((.*)\\)");
	private static final Pattern regionAliasPattern = Pattern.compile("REGION_ALIAS\\s*\\(\\s*\"\\s*(\\w+)\\s*\"\\s*,\\s*(\\w+)\\s*\\)");
	private static final Pattern stackSizePattern = Pattern.compile("[_]*(?i)stack_size\\s*:\\s*(\\w+)\\s*;");
	private static final Pattern stackSizePattern2 = Pattern.compile("PROVIDE\\s*\\(\\s*[_]*(?i)STACK_SIZE\\s*=\\s*(\\w+)\\)");
	private static final Pattern sectionPattern = Pattern.compile("(\\.\\w+).*:.*\\s*\\{[\\s\\S]*\\}\\s*>\\s*(\\w+)");
	private static final Pattern sectionPhyMemoryPattern = Pattern.compile("AT>(\\w+)");

	public void addMemory(Memory m) {
		this.memories.add(m);
	}

	public Memory getMemory(String name) {
		for (Memory m : this.memories) {
			if (m.getName().equals(name))
				return m;
		}
		return null;
	}
	
	public Memory getMemoryByOldName(String name) {
		for (Memory m : this.memories) {
			if (m.getOldName().equals(name))
				return m;
		}
		return null;
	}
	
	public void deleteMemory(String name) {
		int index = -1;
		for (Memory m : this.memories) {
			if (m.getName().equals(name)) {
				index = this.memories.indexOf(m);
			}
		}
		if (index != -1)
			this.memories.remove(index);
	}
	
	public void setMemoryAliasName(String name, String aliasName) {
		for (Memory m : this.memories) {
			if (m.getName().equals(name)) {
				m.setAliasName(aliasName);
			}
		}
	}

	public List<Memory> getMemoryRanges() {
		return this.memories;
	}

	public List<String> getMemoryRangeNames() {
		List<String> names = new ArrayList<String>();
		for (Memory m : this.memories) {
			names.add(m.getRealName());
		}
		return names;
	}

	public void addMemRangeSection(MemRangeSection s) {
		this.memRangeSections.add(s);
	}

	public List<MemRangeSection> getMemRangeSections() {
		return this.memRangeSections;
	}

	public List<String> getSectionNames() {
		List<String> names = new ArrayList<String>();
		for (MemRangeSection section : this.memRangeSections) {
			names.add(section.getSectionName());
		}
		return names;
	}

	public void deleteSection(String name) {
		int index = -1;
		for (MemRangeSection section : this.memRangeSections) {
			if (section.getSectionName().equals(name)) {
				index = this.memRangeSections.indexOf(section);
			}
		}
		if (index != -1)
			this.memRangeSections.remove(index);
	}

	public MemRangeSection getMemRangeSection(String name) {
		for (MemRangeSection s : this.memRangeSections) {
			if (s.getSectionName().equals(name))
				return s;
		}
		return null;
	}
	
	public MemRangeSection getMemRangeSectionByOldName(String name) {
		for (MemRangeSection s : this.memRangeSections) {
			if (s.getOldSectionName().equals(name))
				return s;
		}
		return null;
	}

	public void setStackSize(String size) {
		this.stacksize = size;
	}

	public String getStackSize() {
		if (this.stacksize != null)
			return this.stacksize;
		return null;
	}

	public void setHeapSize(String heapsize) {
		this.heapsize = heapsize;
	}

	public String getHeapSize() {
		if (this.heapsize != null)
			return this.heapsize;
		return null;
	}

	public void clear() {
		this.memories.clear();
		this.memRangeSections.clear();
	}

	public void parseString(String scriptContent) {
		this.clear();
		String temp = "";
		String[] lines = scriptContent.split("\n");
		for (String line : lines) {
			temp += line + "\n";
			if (temp.contains("/*") && temp.contains("*/")) {
				temp = temp.substring(0, temp.indexOf("/*")) + temp.substring(temp.indexOf("*/") + 2, temp.length()); 
			}
			Matcher memoryRegionMatcher = memoryRegionPattern.matcher(temp);
			Matcher regionAliasMatcher = regionAliasPattern.matcher(temp);
			Matcher stackSizeMatcher = stackSizePattern.matcher(temp);
			Matcher stackSizeMatcher2 = stackSizePattern2.matcher(temp);
			Matcher sectionMatcher = sectionPattern.matcher(temp);
			if (memoryRegionMatcher.find()) {
				temp = temp.substring(memoryRegionMatcher.start());
				String name = memoryRegionMatcher.group(1);
				String base = memoryRegionMatcher.group(2);
				String size = memoryRegionMatcher.group(3);
				Memory mem = new Memory(name, base, size);
				Matcher memoryRegionAttrMatcher = memoryRegionAttrPattern.matcher(temp);
				if(memoryRegionAttrMatcher.find()) {
					mem.setAttr(memoryRegionAttrMatcher.group(1));
				}
				this.addMemory(mem);
				temp = "";
			} else if (regionAliasMatcher.find()) {
				temp = temp.substring(regionAliasMatcher.start());
				String aliasName = regionAliasMatcher.group(1);
				String origName = regionAliasMatcher.group(2);
				this.setMemoryAliasName(origName, aliasName);
				temp = "";
			} else if (stackSizeMatcher.find()) {
				temp = temp.substring(stackSizeMatcher.start());
				String stackSize = stackSizeMatcher.group(1);
				this.setStackSize(stackSize);
				temp = "";
			} else if (stackSizeMatcher2.find()) {
				temp = temp.substring(stackSizeMatcher2.start());
				String stackSize = stackSizeMatcher2.group(1);
				this.setStackSize(stackSize);
				temp = "";
			} else if (sectionMatcher.find()) {
				temp = temp.substring(sectionMatcher.start());
				String sectionName = sectionMatcher.group(1);
				String memRegion = sectionMatcher.group(2);
				MemRangeSection section = new MemRangeSection(sectionName, memRegion);
				Matcher sectionPhyMemoryMatcher = sectionPhyMemoryPattern.matcher(temp);
				if (sectionPhyMemoryMatcher.find()) {
					section.setPhyMemory(sectionPhyMemoryMatcher.group(1));
				}
				this.addMemRangeSection(section);
				temp = "";
			}
		}
	}

	public String toString(String scriptContent) {
		List<String> sectionNames = new ArrayList<String>();
		String lastSection = "";
		String newScriptContent = "";
		String temp = "";
		String[] lines = scriptContent.split("\n");
		for (String line : lines) {
			temp += line + "\n";
			Matcher memoryRegionAreaMatcher = memoryRegionAreaPattern.matcher(temp);
			Matcher regionAliasMatcher = regionAliasPattern.matcher(temp);
			Matcher stackSizeMatcher = stackSizePattern.matcher(temp);
			Matcher stackSizeMatcher2 = stackSizePattern2.matcher(temp);
			Matcher sectionMatcher = sectionPattern.matcher(temp);
			if (memoryRegionAreaMatcher.find()) {
				newScriptContent += temp.substring(0, memoryRegionAreaMatcher.start());
				newScriptContent += getMemoryRagionString();
				temp = "";
			} else if (regionAliasMatcher.find()) {
				newScriptContent += temp.substring(0, regionAliasMatcher.start());
				temp = temp.substring(regionAliasMatcher.start());
				String aliasName = regionAliasMatcher.group(1);
				String origName = regionAliasMatcher.group(2);
				Memory mem = this.getMemoryByOldName(origName);
				if (mem != null) {
					temp = temp.replace(aliasName, mem.getAliasName());
				}
				newScriptContent += temp;
				temp = "";
			} else if (stackSizeMatcher.find()) {
				newScriptContent += temp.substring(0, stackSizeMatcher.start());
				temp = temp.substring(stackSizeMatcher.start());
				String stackSize = stackSizeMatcher.group(1);
				temp = temp.replace(stackSize, this.getStackSize());
				newScriptContent += temp;
				temp = "";
			} else if (stackSizeMatcher2.find()) {
				newScriptContent += temp.substring(0, stackSizeMatcher2.start());
				temp = temp.substring(stackSizeMatcher2.start());
				String stackSize = stackSizeMatcher2.group(1);
				temp = temp.replace(stackSize, this.getStackSize());
				newScriptContent += temp;
				temp = "";
			} else if (sectionMatcher.find()) {
				newScriptContent += temp.substring(0, sectionMatcher.start());
				temp = temp.substring(sectionMatcher.start());
				String oldName = sectionMatcher.group(1);
				MemRangeSection section = this.getMemRangeSectionByOldName(oldName);
				if (section == null) {
					temp = "";
					continue;
				}
				// replace memory first, otherwise the index might not match!
				temp = temp.substring(0, sectionMatcher.start(2) - sectionMatcher.start()) + section.getMemoryRange() + temp.substring(sectionMatcher.end(2) - sectionMatcher.start());
				String sectionName = section.getSectionName();
				sectionNames.add(sectionName);
				if (!sectionName.equals(oldName)) {
					section.setOldSectionName(sectionName);
					temp = temp.replace(oldName, sectionName);
				}
//				Matcher sectionPhyMemoryMatcher = sectionPhyMemoryPattern.matcher(temp);
//				if (sectionPhyMemoryMatcher.find()) {
//					temp = temp.substring(0, sectionPhyMemoryMatcher.start(1)) + section.getPhyMemory() + temp.substring(sectionPhyMemoryMatcher.end(1));
//				}
				lastSection = temp;
				newScriptContent += temp;
				temp = "";
			}
		}

		if (!temp.isEmpty()) {
			newScriptContent += temp;
		}

		String newSections = getNewSectionString(sectionNames);
		if (!newSections.isEmpty()) {
			newScriptContent = newScriptContent.replace(lastSection, lastSection + newSections);
		}
		
		return newScriptContent;
	}

	public String getMemoryRagionString() {
		String ret = "MEMORY\n{\n";
		for (Memory mem : this.getMemoryRanges()) {
			if (!mem.getAttr().equals("unknown")) {
				ret += String.format("\t%s (%s) : ORIGIN = %s, LENGTH = %s\n", mem.getName(), mem.getAttr(), mem.getBase(), mem.getSize());
			} else {
				ret += String.format("\t%s : ORIGIN = %s, LENGTH = %s\n", mem.getName(), mem.getBase(), mem.getSize());
			}
		}
		ret += "}\n";
		return ret;
	}

	public String getNewSectionString(List<String> sectionNames) {
		String ret = "";
		for (MemRangeSection section : this.getMemRangeSections()) {
			String name = section.getSectionName();
			String mem = section.getMemoryRange();
			if (!sectionNames.contains(name)) {
				ret += String.format("\t.%s :\n\t{\n\t} > %s AT>%s\n", name, mem, mem);
			}
		}
		if (!ret.isEmpty())
			ret = "\n" + ret;
		return ret;
	}
}
